﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Practices.Unity;
using NHibernate;
using NHibernate.Criterion;
using Epona.Domain;
using Epona.Domain.Query;
using NamedParameters = Epona.Domain.Query.NamedParameters;

namespace Epona.NHibernateRepository
{
    public class NHibernateRepository:IRepository
    {
        //TODO 构造函数注入
        [Dependency]
        public ISessionProvider SessionProvider { get; set; }

        public CriteriaQuery CreateCriteriaQuery<T>() where T : IEntity
        {
            return new CriteriaQuery(this, typeof(T));
        }

        public IList<T> Find<T>(CriteriaQuery criteriaQuery) where T : IEntity
        {
            IQuery query= SessionProvider.Session.CreateQuery(criteriaQuery.GetQueryString());
            ProcessQuery(query, criteriaQuery.GetParameters(), criteriaQuery.FirstResult, criteriaQuery.MaxResults);
            return query.List<T>();
        }

        public T GetSingleResult<T>(CriteriaQuery criteriaQuery) where T : IEntity
        {
            IList<T> results = Find<T>(criteriaQuery);
            return results == null || results.Count == 0 ? default(T) : results[0];
;
        }

        public IList<T> FindByProperty<T>(string propertyName, object propertyValue) where T : IEntity
        {
            return Find<T>(new CriteriaQuery(this,typeof(T)).Eq(propertyName, propertyValue));
        }

        public IList<T> FindByProperties<T>(NamedParameters parameters) where T : IEntity
        {
            CriteriaQuery criteriaQuery = new CriteriaQuery(this, typeof(T));
            foreach (KeyValuePair<string, object> each in parameters.Param)
            {
                criteriaQuery = criteriaQuery.Eq(each.Key, each.Value);
            }
            return Find<T>(criteriaQuery);
        }

        public string GetQueryStringOfNamedQuery(string queryName)
        {
            IQuery query = SessionProvider.Session.GetNamedQuery(queryName);
            return query.QueryString;
        }

        public T Save<T>(T entity) where T : IEntity
        {
            if (entity.NotExisted)
            {
                SessionProvider.Session.Save(entity);
                return entity;
            }
            SessionProvider.Session.Update(entity);
            return entity;
        }

        public void Remove(IEntity entity)
        {
            SessionProvider.Session.Delete(entity);
        }

        public bool Exists<T>(int id) where T : IEntity
        {
            return Get<T>(id) != null;
        }

        public T Get<T>(int id) where T : IEntity
        {
            return SessionProvider.Session.Get<T>(id);
        }

        public T Load<T>(int id) where T : IEntity
        {
            return SessionProvider.Session.Load<T>(id);
        }

        public T GetUnmodified<T>(T entity) where T : IEntity
        {
            SessionProvider.Session.Evict(entity);
            return Get<T>(entity.Id);
        }

        public IList<T> FindAll<T>() where T : IEntity
        {
            return SessionProvider.Session.CreateCriteria(typeof(T)).List<T>();
        }

        public void Flush()
        {
            SessionProvider.Session.Flush();
        }

        public void Refresh(IEntity entity)
        {
            SessionProvider.Session.Refresh(entity);
        }

        public void Clear()
        {
            SessionProvider.Session.Clear();
        }

        private void ProcessQuery(IQuery query,BaseQuery originQuery )
        {
            ProcessQuery(query, originQuery.Parameters, originQuery.FirstResult, originQuery.MaxResults);
            FillParameters(query, originQuery.Parameters);
            query.SetFirstResult(originQuery.FirstResult);
            if(originQuery.MaxResults>0)
            {
                query.SetMaxResults(originQuery.MaxResults);
            }
        }

        private void ProcessQuery(IQuery query,IQueryParameters parameters,int firstResult,int maxResults)
        {
            FillParameters(query, parameters);
            query.SetFirstResult(firstResult);
            if(maxResults>0)
            {
                query.SetMaxResults(maxResults);
            }
        }

        private void FillParameters(IQuery query,IQueryParameters parameters)
        {
            if (parameters == null) return;
            if(parameters.GetType()==typeof(PositionalParameters))
            {
                FillParameters(query, (PositionalParameters)parameters);
            }
            else if(parameters.GetType()==typeof(NamedParameters))
            {
                FillParameters(query, (NamedParameters)parameters);
            }
            else
            {
                throw new Exception("不支持的参数形式");
            }
        }

        private void FillParameters(IQuery query,PositionalParameters parameters)
        {
            object[] paramArray = parameters.Param;
            for(int i=0;i<paramArray.Length;i++)
            {
                query = query.SetParameter(i, paramArray[i]);
            }
        }

        private void FillParameters(IQuery query,NamedParameters parameters)
        {
            foreach(KeyValuePair<string,object> entry in parameters.Param)
            {
                object value=entry.Value;
                if(value.GetType()==typeof(IEnumerable))
                {
                    query.SetParameterList(entry.Key, value as IEnumerable);
                }
                else if(value.GetType()==typeof(Array))
                {
                    query.SetParameterList(entry.Key, value as object[]);
                }
                else
                {
                    query.SetParameter(entry.Key, value);
                }
            }
        }
    }
}
